import { observer } from './observer/index'
import Watcher from './observer/watcher';
import { isObject } from './utils';
//初始化状态(props状态,methods状态,data状态,computed状态,watch状态,)
export function initState(vm) {
    //获取当前实例对象的配置对象
    const opts = vm.$options;
    //根据不同的数据类型,调用不同的初始化逻辑
    if (opts.props) {
        initProps(vm)
    }
    if (opts.methods) {
        initMethods(vm)
    }
    if (opts.data) {
        initData(vm)
    }
    if (opts.computed) {
        initComputed(vm, opts.computed)
    }
    if (opts.watch) {
        initWatch(vm, opts.watch)
    }

}

//初始化props
function initProps() {

}
//初始化Methods
function initMethods() {

}

//初始化data
function initData(vm) {
    //console.log(vm);
    //获取实例化对象vm 中 配置对象($options) 的data函数
    let data = vm.$options.data;
    //判断data是否为函数
    //如果是函数 将this指向 定死为 当前的实例对象(vm),自调用data函数并将返回值作为对象返回
    //不是函数,则返回原对象data
    //定义一个data/vm._data获得的data数据
    data = vm._data = typeof data === 'function' ? data.call(vm) : data
    //遍历数据=>使用自定义代理,将vm._data.name => vm.name
    for (let key in data) {
        proxy(vm, '_data', key)
    }
    //观察者模式--观察data
    observer(data)
}

//初始化Computed
function initComputed(vm, computed) {
    //_computedWatchers 用来存储所有计算属性对应watcher
    const watchers = vm._computedWatchers = {}
    for (let key in computed) {
        const userDef = computed[key];//获取用户的定义的函数
        const getter = typeof userDef === 'function' ? userDef : userDef.get;
        //获取getter函数,lazy: true 表示是计算属性的watcher
        //根据lazy属性,不去调用getter
        watchers[key] = new Watcher(vm, getter, () => { }, { lazy: true })

        //计算属性可以直接通过vm来进行取值,所以将属性定义在实例上
        defineComputed(vm, key, userDef)
    }
}
//属性描述器
const sharedPropertyDefintion = {
    enumerable: true,//可枚举
    configurable: true,//可修改
    get: () => { },
    set: () => { },
}
//定义计算属性方法到vm上
function defineComputed(target, key, userDef) {
    if (typeof userDef === 'function') {
        //sharedPropertyDefintion.get = userDef
        sharedPropertyDefintion.get = createComputedGetter(key)
    } else {
        sharedPropertyDefintion.get = createComputedGetter(key)
        //sharedPropertyDefintion.get = userDef.get
        sharedPropertyDefintion.set = userDef.set || (() => { });
    }
    //数据劫持计算属性
    Object.defineProperty(target, key, sharedPropertyDefintion)
}
//定义computed缓存效果
function createComputedGetter(key) {
    return function (){
        //console.log('xxx');
        //获取之前的watcher
        let watcher=this._computedWatchers[key];
        if(watcher.dirty){//默认第一次,dirty为true 就调用自身的方法
            watcher.evalute()
        }
        if(Dep.target){
            watcher.depend();//收集渲染wather,一起变化
        }
        return watcher.value
    }
}



//初始化Watch,原理：通过Watcher更新,
//扁平化接口
function initWatch(vm, watch) {
    //console.log(vm, watch);
    for (let key in watch) {
        const handler = watch[key];
        if (Array.isArray(handler)) {
            //用法3:watch键值对--数组形式
            //如果是数组,循环数组，将数组的值依次创建Watcher
            for (let i = 0; i < handler.length; i++) {
                //vm =>实例对对象, key==>name, handler[i]==>{ handler: "handler",deep: true,}
                createWatcher(vm, key, handler[i])
            }

        } else {
            //用法2：watch键值对形式---对象
            //vm =>实例对对象, key==>name, handler==>{ handler: (newVal,oldVal)=>console.log(newVal,oldVal),deep: true,}
            createWatcher(vm, key, handler)
        }
    }


}



function createWatcher(vm, key, handler, options) {
    //用法2：watch键值对形式---对象
    if (isObject(handler)) {
        options = handler//options={ handler: (newVal,oldVal)=>console.log(newVal,oldVal),deep: true,}
        handler = handler.handler
    }

    //函数形式:handler=(newVal,oldVal)=>console.log(newVal,oldVal)
    //字符串形式:handler='handler',handler去methods中寻找,转化成函数形式
    if (typeof handler === 'string') {
        handler = vm.$options.methods[handler]
    }
    //console.log(handler, options);
    //watch的原理就是$watch
    return vm.$watch(key, handler, options)
}


export function stateMixin(Vue) {
    Vue.prototype.$watch = function (exprOrFn, cb, options) {
        //console.log(exprOrFn, cb, options);
        const vm = this;
        //判断是用户Watcher的标识, 除此之外还有一个渲染Watcher
        options.user = true;
        new Watcher(vm, exprOrFn, cb, options)
    }
}

//自定义代理拦截
//vm.name => vm._data.name
function proxy(vm, source, key) {
    Object.defineProperty(vm, key, {
        get() {
            //console.log(vm[source][key]);
            return vm[source][key];
        },
        set(newVal) {
            vm[source][key] = newVal;
        }
    })
}
